#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
甘特图绘制优化脚本
基于FJSP的draw方法，但对标签显示进行了优化
"""

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import os
import matplotlib as mpl
import warnings

# 过滤掉特定的警告
warnings.filterwarnings("ignore", message="Blended transforms not yet supported")

def draw_improved_gantt(excel_path):
    """
    从指定路径的Excel文件绘制优化的甘特图
    
    Args:
        excel_path: Excel文件的绝对路径
    """
    if not os.path.exists(excel_path):
        print(f"错误: 文件 {excel_path} 不存在")
        return
    
    try:
        # 设置中文字体支持
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS', 'DejaVu Sans']
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
        
        # 读取调度数据
        dd = pd.read_excel(excel_path)
        print(f"读取到的工序总数: {len(dd)}")
        
        # 提取数据
        job = dd['订单编号'].values.reshape(1, -1) - 1  # 转换为从0开始的索引
        order_num = int(job.max()) + 1  # 订单数量
        list_M = dd['机器编号'].tolist()
        list_S = dd['工序开始时间'].tolist()
        list_W = dd['工序加工时间'].tolist()
        
        # 计算每个工序的完工时间
        finish_times = [list_S[i] + list_W[i] for i in range(len(list_S))]
        actual_finish_time = max(finish_times) if finish_times else 0
        
        # 计算实际使用的最大机器编号
        machine_num = max(list_M) if list_M else 0
        print(f"最大机器编号: {machine_num}")
        
        # 创建图表
        figure, ax = plt.subplots(figsize=(25, 10))
        count = np.zeros((1, order_num))
        
        # 预定义颜色列表
        colors = [
            # 蓝色系列
            '#1f77b4', '#4c78a8', '#6395b9', '#9ecae1', '#c6dbef',
            # 红色系列
            '#d62728', '#e45756', '#f67e7d', '#fc9272', '#fee0d2',
            # 紫色系列
            '#9467bd', '#8c6bb1', '#9e9ac8', '#bcbddc', '#dadaeb',
            # 橙色系列
            '#ff7f0e', '#f58518', '#fd8d3c', '#fdae6b', '#fdd0a2',
            # 棕色系列
            '#8c564b', '#a05d56', '#bd6b63', '#d8856a', '#e5ae83',
            # 灰色系列
            '#7f7f7f', '#969696', '#bdbdbd', '#d9d9d9', '#f0f0f0',
            # 青色系列
            '#17becf', '#6dccda', '#a5e0e3', '#b7e6dc', '#d3f2df',
            # 绿色系列
            '#2ca02c', '#5cb85c', '#78c679', '#a1d99b', '#c7e9c0',
            # 其他辅助色
            '#3a4cc0', '#674ea7', '#8864b0', '#b14d57', '#c44e52',
            '#31a354', '#6c7c0e', '#507c36', '#7e4e90', '#995688',
            '#756bb1', '#636363', '#525252', '#6b6ecf', '#9c9ede'
        ]
        
        # 记录实际绘制的工序数
        drawn_tasks = 0
        
        # 绘制每道工序
        for i in range(job.shape[1]):
            count[0, int(job[0, i])] += 1
            
            # 使用订单编号索引颜色列表，如果超出范围则循环使用颜色
            color_idx = int(job[0, i]) % len(colors)
            icolor = colors[color_idx]
            
            # 绘制工序条形图 - 注意这里使用的坐标系
            bar_height = 0.5
            bar = ax.barh(y=list_M[i], width=list_W[i], height=bar_height, 
                   left=list_S[i], color=icolor, edgecolor='black')
            
            # 获取绘制的条形图实际位置
            bar_y = bar[0].get_y()
            block_width = list_W[i]
            
            # 动态计算字体大小 (根据工序长度调整)
            label_text = f'{int(job[0, i]+1)}.{int(count[0, int(job[0, i])])}'
            char_count = len(label_text)
            
            # 确定最大可用字体大小（基于时间块宽度和字符数）
            if block_width < 0.8:  # 非常小的块，不显示标签
                continue
                
            # 计算能放下所有字符所需的最小字体大小
            min_required_fontsize = 8  # 最小可读字体
            
            # 计算块宽度能支持的最大字体大小
            char_width_at_12pt = 0.6
            max_possible_fontsize = (block_width / (char_count * char_width_at_12pt)) * 12
            
            # 最终字体大小: 在可读范围内的最大可能值
            fontsize = min(20, max(min_required_fontsize, max_possible_fontsize))
            
            # 计算标签的位置 - 精确定位在时间块内部中央
            label_x = list_S[i] + block_width / 2
            # 计算条形图中心点而不是边界上的点
            label_y = bar_y + bar_height / 2
            
            # 只有当字体足够显示时才添加标签
            if fontsize >= min_required_fontsize:
                # 添加工序标签（居中显示，无背景）
                ax.text(label_x, label_y, label_text, 
                        color='black', fontsize=fontsize, weight='bold',
                        ha='center', va='center')  # 水平和垂直居中
            
            drawn_tasks += 1

        print(f"实际绘制的工序数: {drawn_tasks}")

        # 显示完工时间线
        ax.axvline(x=actual_finish_time, color='black', linestyle='-.',
                  label=f'完工时间={actual_finish_time:.1f}')

        # 设置图表格式
        font1 = {'family': 'SimHei', 'weight': 'bold', 'size': 22}
        ax.set_xlabel("加工时间", font1)
        ax.set_title("甘特图", font1)
        ax.set_ylabel("机器", font1)

        # 直接创建与实际机器编号相匹配的Y轴刻度
        machine_labels = []
        machine_ticks = []
        for i in range(1, int(machine_num) + 1):
            machine_ticks.append(i)
            machine_labels.append(f'M{i}')
        
        plt.yticks(machine_ticks, machine_labels)
        
        # 设置x轴范围确保所有工序可见
        plt.xlim(0, actual_finish_time*1.1)
        
        # 设置y轴范围确保所有机器行可见
        plt.ylim(0.5, machine_num + 0.5)
        
        plt.tick_params(labelsize=22)
        labels = ax.get_xticklabels()
        [label.set_fontname('SimHei') for label in labels]
        
        # 添加图例并显示
        plt.legend(prop=font1)
        plt.tight_layout()
        
        # 保存SVG格式
        output_dir = os.path.dirname(excel_path)
        output_path_svg = os.path.join(output_dir, "优化甘特图.svg")
        
        plt.savefig(output_path_svg, bbox_inches='tight')
        
        print(f"甘特图已保存为SVG: {output_path_svg}")
        
        # 显示图表
        plt.show()
        
    except Exception as e:
        print(f"绘制甘特图时出错: {e}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    # 定义要读取的Excel文件路径（用户可以修改此路径）
    EXCEL_PATH = r"C:\Users\L\Downloads\NSGA2_Workshop_rescheduling-main\experiments\exp_20250315_140702\重调度结果.xlsx"
    
    # 打印可用字体
    print("系统可用字体:")
    fonts_found = 0
    for font in mpl.font_manager.findSystemFonts():
        try:
            font_name = mpl.font_manager.FontProperties(fname=font).get_name()
            # 只打印中文相关字体
            if any(name in font_name for name in ['SimHei', 'Microsoft', 'YaHei', 'SimSun', 'Song', '黑体', '宋体', '微软']):
                print(f" - {font_name} ({font})")
                fonts_found += 1
                
            # 限制打印的字体数量，避免输出太多
            if fonts_found >= 20:
                print(" ... 更多字体省略 ...")
                break
        except:
            pass
    
    # 绘制甘特图
    draw_improved_gantt(EXCEL_PATH) 